package org.example.web.exception.handle;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.example.web.exception.AbstractException;
import org.example.web.result.BaseErrorCode;
import org.example.web.result.ResultInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.bind.BindException;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 全局异常捕获类
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(value = AbstractException.class)
    public ResultInfo abstractExceptionHandle(HttpServletRequest request, AbstractException ex) {
        logger.error("========================================== AbstractException-Start ==========================================");
        String params = getRequestParams(request);
        logger.error("RequestURL     : {}", request.getRequestURL());
        logger.error("HTTP Method    : {}", request.getMethod());
        logger.error("Params         : {}", params);
        logger.error("IP             : {}", request.getRemoteAddr());
        logger.error("Cause          : ", ex);
        logger.error("ExMessage      : {}", ex.getMessage());
        logger.info("=========================================== AbstractException-End ===========================================");
        return ResultInfo.error(ex);
    }



    @ExceptionHandler(value = Exception.class)
    public ResultInfo exceptionHandle(HttpServletRequest request, Exception ex) {
        logger.error("========================================== Exception-Start ==========================================");
        String params = getRequestParams(request);
        logger.error("RequestURL     : {}", request.getRequestURL());
        logger.error("HTTP Method    : {}", request.getMethod());
        logger.error("Params         : {}", params);
        logger.error("IP             : {}", request.getRemoteAddr());
        logger.error("Cause          : ", ex);
        logger.error("ExMessage      : {}", ex.getMessage());
        logger.info("=========================================== Exception-End ===========================================");
        return ResultInfo.error();
    }

    /**
     * Valid 校验
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResultInfo validExceptionHandle(HttpServletRequest request, MethodArgumentNotValidException ex) {
        logger.error("========================================== MethodArgumentNotValidException-Start ==========================================");
        String params = getRequestParams(request);
        BindingResult bindingResult = ex.getBindingResult();
        FieldError firstFieldError = CollUtil.getFirst(bindingResult.getFieldErrors());
        String exceptionStr = Optional.ofNullable(firstFieldError)
                .map(FieldError::getDefaultMessage)
                .orElse(StrUtil.EMPTY);
        logger.error("RequestURL     : {}", request.getRequestURL());
        logger.error("HTTP Method    : {}", request.getMethod());
        logger.error("Params         : {}", params);
        logger.error("IP             : {}", request.getRemoteAddr());
        logger.error("Cause          : ", ex);
        logger.error("ExMessage      : {}", exceptionStr);
        logger.info("=========================================== MethodArgumentNotValidException-End ===========================================");
        return ResultInfo.error(BaseErrorCode.PARAMETER_IS_INCORRECT_PLEASE_FILL_IN_AGAIN.code(), exceptionStr);
    }

    /** 捕获 BindException (通常用于表单绑定的错误) */
    @ExceptionHandler(BindException.class)
    public ResultInfo handleBindException(HttpServletRequest request, Exception ex){
        logger.error("========================================== BindException-Start ==========================================");
        String params = getRequestParams(request);
        logger.error("RequestURL     : {}", request.getRequestURL());
        logger.error("HTTP Method    : {}", request.getMethod());
        logger.error("Params         : {}", params);
        logger.error("IP             : {}", request.getRemoteAddr());
        logger.error("Cause          : ", ex);
        logger.error("ExMessage      : {}", ex.getMessage());
        logger.info("=========================================== BindException-End ===========================================");
        return ResultInfo.error(BaseErrorCode.PARAMETER_IS_INCORRECT_PLEASE_FILL_IN_AGAIN);
    }

    /** 通常由于请求体无法解析 */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResultInfo handleHttpMessageNotReadableException(HttpServletRequest request, Exception ex){
        logger.error("========================================== handleHttpMessageNotReadableException-Start ==========================================");
        String params = getRequestParams(request);
        logger.error("RequestURL     : {}", request.getRequestURL());
        logger.error("HTTP Method    : {}", request.getMethod());
        logger.error("Params         : {}", params);
        logger.error("IP             : {}", request.getRemoteAddr());
        logger.error("Cause          : ", ex);
        logger.error("ExMessage      : {}", ex.getMessage());
        logger.info("=========================================== handleHttpMessageNotReadableException-End ===========================================");
        return ResultInfo.error(BaseErrorCode.PARAMETER_IS_INCORRECT_PLEASE_FILL_IN_AGAIN);
    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResultInfo handleHttpRequestMethodNotSupported(HttpServletRequest request, HttpRequestMethodNotSupportedException ex) {
        logger.error("========================================== HttpRequestMethodNotSupportedException-Start ==========================================");
        String params = getRequestParams(request);
        logger.error("RequestURL     : {}", request.getRequestURL());
        logger.error("HTTP Method    : {}", request.getMethod());
        logger.error("Params         : {}", params);
        logger.error("IP             : {}", request.getRemoteAddr());
        logger.error("Cause          : ", ex);
        logger.error("ExMessage      : {}", ex.getMessage());
        logger.info("=========================================== HttpRequestMethodNotSupportedException-End ===========================================");
        return ResultInfo.error(BaseErrorCode.INCORRECT_REQUEST_MODE);
    }

    private String getRequestParams(HttpServletRequest request) {
        if (HttpMethod.POST.name().equalsIgnoreCase(request.getMethod()) && StringUtils.startsWithIgnoreCase(MediaType.APPLICATION_JSON_VALUE, request.getContentType())) {
            return getJsonPostBody(request);
        } else {
            return request.getParameterMap().entrySet().stream()
                    .map(entry -> entry.getKey() + "=" + String.join(",", entry.getValue()))
                    .collect(Collectors.joining(", "));
        }
    }

    private String getJsonPostBody(HttpServletRequest request) {
        StringBuilder jsonBody = new StringBuilder();
        try (BufferedReader reader = request.getReader()) {
            String line;
            while ((line = reader.readLine()) != null) {
                jsonBody.append(line);
            }
        } catch (IOException e) {
            logger.error("Error reading the request body", e);
        }
        return jsonBody.toString();
    }
}
